#include <kuai/misc/error.hpp>
#include <kuai/mol/space.hpp>
#include <kuai/mol/molid.hpp>


namespace kuai {

	PropertyConnectTable::PropertyConnectTable(const Index buffer[]) 
		: PropertyIndex(ConnectTable::buffer_size(buffer[0], buffer[1]), 0)
	{ 
		ConnectTable::makeBuffer(buffer, &data[0]);
		ct = ConnectTable(&data[0]);
	}

	SimObjPtr& PhaseSpace::operator[](const String& key) {
		iterator it = find(key);
		if (it == end()) {
			SimObjPtr obj = SimObjFactory::getInstance().create(key, *this);			
			insert(std::make_pair(key, obj));
			it = find(key);
		}

		return it->second;
	}

	

	boost::shared_ptr<SimObject> SimObjFactory::create(const String& name, PhaseSpace& space) const {
		std::map<String, SimObjectCreator>::const_iterator it = funcs.find(name);
		SimObject* p = NULL;
		if (it != funcs.end()) {
			p = it->second(space);
		}

		return boost::shared_ptr<SimObject>(p);	
	}

	SimObjFactory& SimObjFactory::getInstance() 
	{
		static SimObjFactory result;
		return result;
	}

	PhaseSpace::const_iterator PhaseSpace::find(const String& key) const {
		std::map<String, Index>::const_iterator it = indexes.find(key);
		if (it == indexes.end()) {
			return end();
		}
		else {
			return begin() + it->second;
		}
	}
	PhaseSpace::iterator PhaseSpace::find(const String& key) {
		std::map<String, Index>::const_iterator it = indexes.find(key);
		if (it == indexes.end()) {
			return end();
		}
		else {
			return begin() + it->second;
		}
	}

	bool PhaseSpace::erase(const String& key) {
		std::map<String, Index>::iterator it = indexes.find(key);
		if (it != indexes.end()) {
			assert (items[it->second].first == key);
			items.erase(items.begin()+it->second);
			indexes.erase(it);
			return true;
		}
		else {
			return false;
		}
	}

	Index setupAromaticBond(PhaseSpace& space) {
		assert (space.find(MOL_INFO_CT) != space.end());
		ConnectTable& ct = static_cast<PropertyConnectTable&>(*(space.find(MOL_INFO_CT)->second)).x_ct();
		Index nBonds = space.countBonds();
		if (nBonds > 0) {
			if (space.find(MOL_INFO_BOND_ORDER) == space.end()) {
				ArrayProperty<Index>* p = new ArrayProperty<Index>(nBonds);
				Index* pp = p->x_ptr();
				for (size_t i = 0; i < p->size(); ++i) {
					pp[i] = ct.getBond(i).order;
				}
				space.insert(MOL_INFO_BOND_ORDER, SimObjPtr(p));
			}

			const Index* prank = NULL;
			if (space.find(MOL_INFO_BOND_RANK) == space.end()) {
				ArrayProperty<Index>* p = new ArrayProperty<Index>(nBonds);
				SimObjPtr ptr(p);
				Index* pp = p->x_ptr();
				ConnectTable::RingFinder::getRingRankOfBonds(ct, pp, ct.countAtoms());
				for (Index i = 0; i < nBonds; ++i) {
					if (pp[i] == INVALID_INDEX) {
						pp[i] = 0;
					}
				}
				space.insert(MOL_INFO_BOND_RANK, ptr);
				prank = p->x_ptr();
			}
			else {
				prank = static_cast<PropertyIndex&>(*space[MOL_INFO_BOND_RANK]).x_ptr();
			}
			const Integer* pchg = NULL;
			std::vector<Integer> charges;
			if (space.find(MOL_INFO_ATOM_CHARGE) == space.end()) {
				charges.resize(nBonds, 0);
				pchg = &charges[0];
			}
			else {
				pchg  = static_cast<PropertyInteger&>(*space[MOL_INFO_ATOM_CHARGE]).c_ptr();
			}

			return setupAromaticBond(ct, prank, pchg);
		}
		else {
			return 0;
		}
	}

	void calcBondNormVector(const ConnectTable& ct, const Bond& bondI, const XYZ coords[], XYZ result[]) {
		Index deg = ct.degree(bondI);
		Index atom1 = ct.indexOf(ct.getAtom(bondI, 0));
		Index atom2 = ct.indexOf(ct.getAtom(bondI, 1));

	}
	void calcBondNormVector(const ConnectTable& ct, const XYZ coords[], XYZ result[]) {
		Index nBonds = ct.countBonds();
		for (Index i = 0; i < nBonds; ++i) {
			calcBondNormVector(ct, ct.getBond(i), coords, result+i);
		}
	}
}

